﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace ToolKit.Mvvm.Bindings
{
    /// <summary>
    /// 普通命名
    /// </summary>
    public class RelayCommand : ICommand
    {
        private readonly Action execute;
        private readonly Func<bool>? canExecute;

        public RelayCommand(Action execute)
        {
            ArgumentNullException.ThrowIfNull(execute, "execute");
            this.execute = execute;
        }

        public RelayCommand(Action execute, Func<bool> canExecute)
        {
            ArgumentNullException.ThrowIfNull(execute, "execute");
            ArgumentNullException.ThrowIfNull(canExecute, "canExecute");
            this.execute = execute;
            this.canExecute = canExecute;
        }


        public event EventHandler? CanExecuteChanged;

        public bool CanExecute(object? parameter)
        {
            return canExecute?.Invoke() ?? true;
        }

        public void Execute(object? parameter)
        {
            execute();
        }
    }


    /// <summary>
    /// 泛型命令
    /// </summary>
    /// <typeparam name="T">参数</typeparam>
    public class RelayCommand<T> : ICommand
    {
        private readonly Action<T> execute;
        private readonly Predicate<T?>? canExecute;

        public RelayCommand(Action<T> execute)
        {
            ArgumentNullException.ThrowIfNull(execute, "execute");
            this.execute = execute;
        }

        public RelayCommand(Action<T> execute, Predicate<T?> canExecute)
        {
            ArgumentNullException.ThrowIfNull(execute, "execute");
            ArgumentNullException.ThrowIfNull(canExecute, "canExecute");
            this.execute = execute;
            this.canExecute = canExecute;
        }


        public event EventHandler? CanExecuteChanged;

        public bool CanExecute(T? parameter)
        {
            return canExecute?.Invoke(parameter) ?? true;
        }

        public bool CanExecute(object? parameter)
        {
            if (parameter == null && default(T) != null)
            {
                return false;
            }

            if (!TryGetCommandArgument(parameter, out var result))
            {
                if (parameter == null)
                    throw new ArgumentException($"{typeof(T)}", "parameter");
                else
                    throw new ArgumentException($"参数{parameter!.GetType()}不是{typeof(T)}", "parameter");
            }

            return CanExecute(result);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Execute(T? parameter)
        {
            execute(parameter);
        }

        public void Execute(object? parameter)
        {
            if (!TryGetCommandArgument(parameter, out var result))
            {
                if (parameter == null)
                    throw new ArgumentException($"{typeof(T)}", "parameter");
                else
                    throw new ArgumentException($"参数{parameter!.GetType()}不是{typeof(T)}", "parameter");
            }

            Execute(result);
        }


        internal static bool TryGetCommandArgument(object? parameter, out T? result)
        {
            if (parameter == null && default(T) == null)
            {
                result = default(T);
                return true;
            }

            if (parameter is T)
            {
                T val = (result = (T)parameter);
                return true;
            }

            result = default(T);
            return false;
        }
    }
}
